/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cn.ac.iie.di.ban.news.server.util;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;

/**
 *
 * @author zwlin
 */
public class KeyInfoKeeper {

    private static Logger logger = Logger.getLogger(KeyInfoKeeper.class);

    private HashSet<String> testKeys;
    private ReentrantReadWriteLock testLock;
    private HashSet<String> onlineKeys;
    private ReentrantReadWriteLock onlineLock;
    private HashSet<String> validKeys;
    private ReentrantReadWriteLock validLock;

    private boolean isActivated;
    private long updateInteval;
    private ArrayBlockingQueue<String> updateHosts;
    private Timer updateTimer;
    private boolean needHttps;

    public KeyInfoKeeper(String updateHost, int updateIntervalSecond, boolean needHttps) {
        this.updateInteval = updateIntervalSecond;
        this.updateHosts = new ArrayBlockingQueue<>(2);
        this.updateHosts.add(updateHost);
        this.needHttps = needHttps;

        testKeys = new HashSet<>();
        testLock = new ReentrantReadWriteLock();
        onlineKeys = new HashSet<>();
        onlineLock = new ReentrantReadWriteLock();
        validKeys = new HashSet<>();
        validLock = new ReentrantReadWriteLock();
        isActivated = false;
        updateTimer = new Timer("KeyInfoUpdateTimer");
    }

    public KeyInfoKeeper(Collection<String> updateHosts, int updateIntervalSecond, boolean needHttps) {
        this.updateInteval = updateIntervalSecond;
        this.updateHosts = new ArrayBlockingQueue<>(updateHosts.size() + 2, false, updateHosts);
        this.needHttps = needHttps;
        testKeys = new HashSet<>();
        testLock = new ReentrantReadWriteLock();
        onlineKeys = new HashSet<>();
        onlineLock = new ReentrantReadWriteLock();
        validKeys = new HashSet<>();
        validLock = new ReentrantReadWriteLock();
        isActivated = false;
        updateTimer = new Timer("KeyInfoUpdateTimer");
    }

    public void start() {
        updateTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                updateKeys();
            }
        }, 0, updateInteval * 1000);
        isActivated = true;
    }

    public void stop() {
        isActivated = false;
        updateTimer.cancel();
    }

    /**
     * fix me
     */
    private void updateKeys() {
        if (!isActivated) {
            return;
        }

        updatekeys("get_test_keys", testKeys, testLock.writeLock());
        updatekeys("get_online_keys", onlineKeys, onlineLock.writeLock());
        updatekeys("get_valid_keys", validKeys, validLock.writeLock());

    }

    private void updatekeys(String query, HashSet<String> set, ReentrantReadWriteLock.WriteLock lock) {
        HashMap<String, String> param = new HashMap<>();
        param.put("type", query);
        HttpUtils.Result result;
        String host = null;
        for (int i = 0; i < updateHosts.size(); ++i) {
            try {
                host = updateHosts.take();
                updateHosts.put(host);
                host = host + "api_" + query;
                if (needHttps) {
                    result = HttpUtils.httpsPost(host, param);
                } else {
                    result = HttpUtils.httpPost(host, param);
                }
                if (result.statusCode != HttpServletResponse.SC_OK) {
                    logger.error("update " + host + " failed because :" + result.reasonPhrase);
                } else {
                    lock.lock();
                    try {
                        set.clear();
                        for (String s : result.content.split("\n")) {
                            set.add(s);
                        }
                        logger.info(query + " updated. newkey:" + result.content.replace('\n', ','));
                    } finally {
                        lock.unlock();
                    }
                }
                return;
            } catch (Exception ex) {
            }
        }
        try {
            host = updateHosts.take();
            updateHosts.put(host);
            host = host + "api_" + query;
            if (needHttps) {
                result = HttpUtils.httpsPost(host, param);
            } else {
                result = HttpUtils.httpPost(host, param);
            }
            if (result.statusCode != HttpServletResponse.SC_OK) {
                logger.error("update " + host + " failed because :" + result.reasonPhrase);
            } else {
                lock.lock();
                try {
                    set.clear();
                    for (String s : result.content.split("\n")) {
                        set.add(s);
                    }
                    logger.info(query + " updated. newkey:" + result.content.replace('\n', ','));
                } finally {
                    lock.unlock();
                }
            }
        } catch (Exception ex) {
            logger.error("update " + query + " failed.", ex);
        }
    }

    public boolean isOnline(String key) {
        if (!isActivated) {
            return false;
        }
        onlineLock.readLock().lock();
        try {
            return onlineKeys.contains(key);
        } finally {
            onlineLock.readLock().unlock();
        }
    }

    public boolean isTest(String key) {
        if (!isActivated) {
            return false;
        }
        testLock.readLock().lock();
        try {
            return testKeys.contains(key);
        } finally {
            testLock.readLock().unlock();
        }
    }

    public boolean isActivated(String key) {
        if (!isActivated) {
            return false;
        }
        validLock.readLock().lock();
        try {
            return validKeys.contains(key);
        } finally {
            validLock.readLock().unlock();
        }
    }
}
